package com.jinly.ryder.ansible.service.impl;

import com.jinly.ryder.ansible.config.AnsibleProperties;
import com.jinly.ryder.ansible.service.AnsibleService;
import com.jinly.ryder.ansible.service.model.AnsibleDeployResult;
import com.jinly.ryder.ansible.service.model.AnsibleParam;
import com.jinly.ryder.ansible.service.model.AnsibleTask;
import com.jinly.ryder.ansible.service.model.AnsibleTaskProgress;
import com.jinly.ryder.common.service.SystemSettingService;
import com.jinly.ryder.common.service.model.setting.SshSetting;
import com.jinly.ryder.common.utils.SshKeyUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.zeroturnaround.exec.ProcessExecutor;
import org.zeroturnaround.exec.ProcessResult;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;


@Slf4j
@Service
@RequiredArgsConstructor
public class AnsibleServiceImpl implements AnsibleService {
    private final AnsibleProperties ansibleProperties;
    private final SystemSettingService systemSettingService;

    @Override
    public AnsibleDeployResult exec(AnsibleParam ansibleParam, Consumer<AnsibleTaskProgress> consumer) {
        AnsibleDeployResult deployResult = new AnsibleDeployResult();
        try {
            String priKey = getPriKey();
            List<AnsibleTask> taskList = execListTask(ansibleParam, priKey);
            AnsibleOutputStream ansibleOutputStream = new AnsibleOutputStream(taskList, consumer);

            List<String> command = createCommand(ansibleParam, priKey);
            Future<ProcessResult> future = new ProcessExecutor()
                    .command(command.stream().toArray(String[]::new))
                    .directory(getWorkDir())
                    .redirectOutput(ansibleOutputStream)
                    .readOutput(true)
                    .start().getFuture();
            ProcessResult result = future.get(600, TimeUnit.SECONDS);

            deployResult.setTaskList(taskList);
            deployResult.setOutput(result.outputUTF8());
        } catch (Exception e) {
            log.error("Ansible执行异常", e);
            deployResult.setSuccess(false);
            deployResult.setOutput(e.getMessage());
        }
        return deployResult;

    }

    private String getPriKey() {
        SshSetting sshSetting = systemSettingService.getSettingItem(SshSetting.class);
        return SshKeyUtil.getPriKeyFileName(sshSetting.getPriKey());
    }


    private File getWorkDir() {
        File workdir = null;
        if (ansibleProperties.getWorkdir() != null) {
            workdir = new File(ansibleProperties.getWorkdir());
        }
        if (workdir == null || !workdir.exists()) {
            workdir = new File(this.getClass().getClassLoader().getResource("ansible").getFile());
        }
        return workdir;
    }


    private List<String> createCommand(AnsibleParam ansibleParam, String priKey) {
        List<String> command = new ArrayList<>();
        command.add("ansible-playbook");//脚本文件
        command.add(ansibleParam.getPlaybook());
        command.add("-i");//Inventory文件
        command.add(ansibleParam.getInventory());
        command.add("--tags");//task tag
        command.add(ansibleParam.getTags());
        command.add("--private-key");
        command.add(priKey);
        command.add("-v");
        ansibleParam.getParamMap().entrySet().stream().forEach(e -> {
            command.add("-e");
            command.add(e.getKey() + "=" + e.getValue());
        });
        return command;
    }


    private List<AnsibleTask> execListTask(AnsibleParam ansibleParam, String priKey) throws Exception {
        List<String> command = createCommand(ansibleParam, priKey);
        command.add("--list-tasks");
        Future<ProcessResult> future = new ProcessExecutor()
                .command(command.stream().toArray(String[]::new))
                .directory(getWorkDir())
                .readOutput(true)
                .start().getFuture();
        ProcessResult result = future.get(120, TimeUnit.SECONDS);
        return Arrays.stream(result.outputUTF8().split("\n"))
                .filter(l -> l.contains("TAGS:") && !l.startsWith("  play"))
                .map(l -> {
                    String taskName = l.trim();
                    taskName = taskName.substring(0, taskName.indexOf("\tTAGS:"));
                    AnsibleTask task = new AnsibleTask();
                    task.setName(taskName);
                    return task;
                }).collect(Collectors.toList());
    }

}
